use std::fmt;
use std::ops;
use glium::uniforms::{AsUniformValue, UniformValue};
use crate::math::vector3::Vector3;

#[derive(Copy, Clone)]
pub struct Vector2(pub f32, pub f32);

impl Vector2 {
    pub fn zero() -> Self {
        Self(0.0, 0.0)
    }

    pub fn dot(self, rhs: Self) -> f32 {
        self.0 * rhs.0 + self.1 * rhs.1
    }

    pub fn as_array(&self) -> [f32; 2] {
        [self.0, self.1]
    }

    pub fn set(&mut self, x: f32, y: f32) {
        self.0 = x;
        self.1 = y;
    }
}

impl AsUniformValue for Vector2 {
    fn as_uniform_value(&self) -> UniformValue {
        UniformValue::Vec2(self.as_array())
    }
}

impl fmt::Display for Vector2 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[{}, {}]", self.0, self.1)
    }
}

impl Into<Vector3> for Vector2 {
    fn into(self) -> Vector3 {
        Vector3(self.0, self.1, 0.0)
    }
}

impl ops::Add for Vector2 {
    type Output = Self;
    fn add(self, rhs: Self) -> Self {
        Self(self.0 + rhs.0, self.1 + rhs.1)
    }
}

impl ops::AddAssign for Vector2 {
    fn add_assign(&mut self, rhs: Self) {
        self.0 += rhs.0;
        self.1 += rhs.1;
    }
}

impl ops::Sub for Vector2 {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self {
        Self(self.0 - rhs.0, self.1 - rhs.1)
    }
}

impl ops::SubAssign for Vector2 {
    fn sub_assign(&mut self, rhs: Self) {
        self.0 -= rhs.0;
        self.1 -= rhs.1;
    }
}

impl ops::Mul<f32> for Vector2 {
    type Output = Self;
    fn mul(self, rhs: f32) -> Self::Output {
        Self(self.0 * rhs, self.1 * rhs)
    }
}

impl ops::MulAssign<f32> for Vector2 {
    fn mul_assign(&mut self, rhs: f32) {
        self.0 *= rhs;
        self.1 *= rhs;
    }
}

impl ops::Mul<Vector2> for f32 {
    type Output = Vector2;
    fn mul(self, rhs: Vector2) -> Self::Output {
        Vector2(self * rhs.0, self * rhs.1)
    }
}

impl ops::DivAssign for Vector2 {
    fn div_assign(&mut self, rhs: Self) {
        self.0 /= rhs.0;
        self.1 /= rhs.1;
    }
}

impl ops::Neg for Vector2 {
    type Output = Self;
    fn neg(self) -> Self {
        Self(-self.0, -self.1)
    }
}